1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/asmjs/asm-js.h"
6 
7 #include "src/api-natives.h"
8 #include "src/api.h"
9 #include "src/asmjs/asm-typer.h"
10 #include "src/asmjs/asm-wasm-builder.h"
11 #include "src/assert-scope.h"
12 #include "src/execution.h"
13 #include "src/factory.h"
14 #include "src/handles.h"
15 #include "src/isolate.h"
16 #include "src/objects.h"
17 #include "src/parsing/parse-info.h"
18 
19 #include "src/wasm/module-decoder.h"
20 #include "src/wasm/wasm-js.h"
21 #include "src/wasm/wasm-module-builder.h"
22 #include "src/wasm/wasm-module.h"
23 #include "src/wasm/wasm-objects.h"
24 #include "src/wasm/wasm-result.h"
25 
26 typedef uint8_t byte;
27 
28 using v8::internal::wasm::ErrorThrower;
29 
30 namespace v8 {
31 namespace internal {
32 
33 namespace {
StdlibMathMember(i::Isolate * isolate,Handle<JSReceiver> stdlib,Handle<Name> name)34 Handle<i::Object> StdlibMathMember(i::Isolate* isolate,
35                                    Handle<JSReceiver> stdlib,
36                                    Handle<Name> name) {
37   if (stdlib.is_null()) {
38     return Handle<i::Object>();
39   }
40   Handle<i::Name> math_name(
41       isolate->factory()->InternalizeOneByteString(STATIC_CHAR_VECTOR("Math")));
42   MaybeHandle<i::Object> maybe_math = i::Object::GetProperty(stdlib, math_name);
43   if (maybe_math.is_null()) {
44     return Handle<i::Object>();
45   }
46   Handle<i::Object> math = maybe_math.ToHandleChecked();
47   if (!math->IsJSReceiver()) {
48     return Handle<i::Object>();
49   }
50   MaybeHandle<i::Object> maybe_value = i::Object::GetProperty(math, name);
51   if (maybe_value.is_null()) {
52     return Handle<i::Object>();
53   }
54   return maybe_value.ToHandleChecked();
55 }
56 
IsStdlibMemberValid(i::Isolate * isolate,Handle<JSReceiver> stdlib,Handle<i::Object> member_id)57 bool IsStdlibMemberValid(i::Isolate* isolate, Handle<JSReceiver> stdlib,
58                          Handle<i::Object> member_id) {
59   int32_t member_kind;
60   if (!member_id->ToInt32(&member_kind)) {
61     UNREACHABLE();
62   }
63   switch (member_kind) {
64     case wasm::AsmTyper::StandardMember::kNone:
65     case wasm::AsmTyper::StandardMember::kModule:
66     case wasm::AsmTyper::StandardMember::kStdlib:
67     case wasm::AsmTyper::StandardMember::kHeap:
68     case wasm::AsmTyper::StandardMember::kFFI: {
69       // Nothing to check for these.
70       return true;
71     }
72     case wasm::AsmTyper::StandardMember::kInfinity: {
73       if (stdlib.is_null()) {
74         return false;
75       }
76       Handle<i::Name> name(isolate->factory()->InternalizeOneByteString(
77           STATIC_CHAR_VECTOR("Infinity")));
78       MaybeHandle<i::Object> maybe_value = i::Object::GetProperty(stdlib, name);
79       if (maybe_value.is_null()) {
80         return false;
81       }
82       Handle<i::Object> value = maybe_value.ToHandleChecked();
83       return value->IsNumber() && std::isinf(value->Number());
84     }
85     case wasm::AsmTyper::StandardMember::kNaN: {
86       if (stdlib.is_null()) {
87         return false;
88       }
89       Handle<i::Name> name(isolate->factory()->InternalizeOneByteString(
90           STATIC_CHAR_VECTOR("NaN")));
91       MaybeHandle<i::Object> maybe_value = i::Object::GetProperty(stdlib, name);
92       if (maybe_value.is_null()) {
93         return false;
94       }
95       Handle<i::Object> value = maybe_value.ToHandleChecked();
96       return value->IsNaN();
97     }
98 #define STDLIB_MATH_FUNC(CamelName, fname)                             \
99   case wasm::AsmTyper::StandardMember::k##CamelName: {                 \
100     Handle<i::Name> name(isolate->factory()->InternalizeOneByteString( \
101         STATIC_CHAR_VECTOR(#fname)));                                  \
102     Handle<i::Object> value = StdlibMathMember(isolate, stdlib, name); \
103     if (value.is_null() || !value->IsJSFunction()) {                   \
104       return false;                                                    \
105     }                                                                  \
106     Handle<i::JSFunction> func(i::JSFunction::cast(*value));           \
107     return func->shared()->code() ==                                   \
108            isolate->builtins()->builtin(Builtins::k##CamelName);       \
109   }
110       STDLIB_MATH_FUNC(MathAcos, acos)
111       STDLIB_MATH_FUNC(MathAsin, asin)
112       STDLIB_MATH_FUNC(MathAtan, atan)
113       STDLIB_MATH_FUNC(MathCos, cos)
114       STDLIB_MATH_FUNC(MathSin, sin)
115       STDLIB_MATH_FUNC(MathTan, tan)
116       STDLIB_MATH_FUNC(MathExp, exp)
117       STDLIB_MATH_FUNC(MathLog, log)
118       STDLIB_MATH_FUNC(MathCeil, ceil)
119       STDLIB_MATH_FUNC(MathFloor, floor)
120       STDLIB_MATH_FUNC(MathSqrt, sqrt)
121       STDLIB_MATH_FUNC(MathAbs, abs)
122       STDLIB_MATH_FUNC(MathClz32, clz32)
123       STDLIB_MATH_FUNC(MathMin, min)
124       STDLIB_MATH_FUNC(MathMax, max)
125       STDLIB_MATH_FUNC(MathAtan2, atan2)
126       STDLIB_MATH_FUNC(MathPow, pow)
127       STDLIB_MATH_FUNC(MathImul, imul)
128       STDLIB_MATH_FUNC(MathFround, fround)
129 #undef STDLIB_MATH_FUNC
130 #define STDLIB_MATH_CONST(cname, const_value)                             \
131   case wasm::AsmTyper::StandardMember::kMath##cname: {                    \
132     i::Handle<i::Name> name(isolate->factory()->InternalizeOneByteString( \
133         STATIC_CHAR_VECTOR(#cname)));                                     \
134     i::Handle<i::Object> value = StdlibMathMember(isolate, stdlib, name); \
135     return !value.is_null() && value->IsNumber() &&                       \
136            value->Number() == const_value;                                \
137   }
138       STDLIB_MATH_CONST(E, 2.718281828459045)
139       STDLIB_MATH_CONST(LN10, 2.302585092994046)
140       STDLIB_MATH_CONST(LN2, 0.6931471805599453)
141       STDLIB_MATH_CONST(LOG2E, 1.4426950408889634)
142       STDLIB_MATH_CONST(LOG10E, 0.4342944819032518)
143       STDLIB_MATH_CONST(PI, 3.141592653589793)
144       STDLIB_MATH_CONST(SQRT1_2, 0.7071067811865476)
145       STDLIB_MATH_CONST(SQRT2, 1.4142135623730951)
146 #undef STDLIB_MATH_CONST
147     default: { UNREACHABLE(); }
148   }
149   return false;
150 }
151 
152 }  // namespace
153 
ConvertAsmToWasm(ParseInfo * info)154 MaybeHandle<FixedArray> AsmJs::ConvertAsmToWasm(ParseInfo* info) {
155   ErrorThrower thrower(info->isolate(), "Asm.js -> WebAssembly conversion");
156   wasm::AsmTyper typer(info->isolate(), info->zone(), *(info->script()),
157                        info->literal());
158   if (!typer.Validate()) {
159     DCHECK(!info->isolate()->has_pending_exception());
160     PrintF("Validation of asm.js module failed: %s", typer.error_message());
161     return MaybeHandle<FixedArray>();
162   }
163   v8::internal::wasm::AsmWasmBuilder builder(info->isolate(), info->zone(),
164                                              info->literal(), &typer);
165   i::Handle<i::FixedArray> foreign_globals;
166   auto asm_wasm_result = builder.Run(&foreign_globals);
167   wasm::ZoneBuffer* module = asm_wasm_result.module_bytes;
168   wasm::ZoneBuffer* asm_offsets = asm_wasm_result.asm_offset_table;
169 
170   i::MaybeHandle<i::JSObject> compiled = wasm::CreateModuleObjectFromBytes(
171       info->isolate(), module->begin(), module->end(), &thrower,
172       internal::wasm::kAsmJsOrigin, info->script(), asm_offsets->begin(),
173       asm_offsets->end());
174   DCHECK(!compiled.is_null());
175 
176   wasm::AsmTyper::StdlibSet uses = typer.StdlibUses();
177   Handle<FixedArray> uses_array =
178       info->isolate()->factory()->NewFixedArray(static_cast<int>(uses.size()));
179   int count = 0;
180   for (auto i : uses) {
181     uses_array->set(count++, Smi::FromInt(i));
182   }
183 
184   Handle<FixedArray> result = info->isolate()->factory()->NewFixedArray(3);
185   result->set(0, *compiled.ToHandleChecked());
186   result->set(1, *foreign_globals);
187   result->set(2, *uses_array);
188   return result;
189 }
190 
IsStdlibValid(i::Isolate * isolate,Handle<FixedArray> wasm_data,Handle<JSReceiver> stdlib)191 bool AsmJs::IsStdlibValid(i::Isolate* isolate, Handle<FixedArray> wasm_data,
192                           Handle<JSReceiver> stdlib) {
193   i::Handle<i::FixedArray> uses(i::FixedArray::cast(wasm_data->get(2)));
194   for (int i = 0; i < uses->length(); ++i) {
195     if (!IsStdlibMemberValid(isolate, stdlib,
196                              uses->GetValueChecked<i::Object>(isolate, i))) {
197       return false;
198     }
199   }
200   return true;
201 }
202 
InstantiateAsmWasm(i::Isolate * isolate,Handle<FixedArray> wasm_data,Handle<JSArrayBuffer> memory,Handle<JSReceiver> foreign)203 MaybeHandle<Object> AsmJs::InstantiateAsmWasm(i::Isolate* isolate,
204                                               Handle<FixedArray> wasm_data,
205                                               Handle<JSArrayBuffer> memory,
206                                               Handle<JSReceiver> foreign) {
207   i::Handle<i::JSObject> module(i::JSObject::cast(wasm_data->get(0)));
208   i::Handle<i::FixedArray> foreign_globals(
209       i::FixedArray::cast(wasm_data->get(1)));
210 
211   ErrorThrower thrower(isolate, "Asm.js -> WebAssembly instantiation");
212 
213   i::MaybeHandle<i::JSObject> maybe_module_object =
214       i::wasm::WasmModule::Instantiate(isolate, &thrower, module, foreign,
215                                        memory);
216   if (maybe_module_object.is_null()) {
217     return MaybeHandle<Object>();
218   }
219 
220   i::Handle<i::Name> init_name(isolate->factory()->InternalizeUtf8String(
221       wasm::AsmWasmBuilder::foreign_init_name));
222 
223   i::Handle<i::Object> module_object = maybe_module_object.ToHandleChecked();
224   i::MaybeHandle<i::Object> maybe_init =
225       i::Object::GetProperty(module_object, init_name);
226   DCHECK(!maybe_init.is_null());
227 
228   i::Handle<i::Object> init = maybe_init.ToHandleChecked();
229   i::Handle<i::Object> undefined(isolate->heap()->undefined_value(), isolate);
230   i::Handle<i::Object>* foreign_args_array =
231       new i::Handle<i::Object>[foreign_globals->length()];
232   for (int j = 0; j < foreign_globals->length(); j++) {
233     if (!foreign.is_null()) {
234       i::MaybeHandle<i::Name> name = i::Object::ToName(
235           isolate, i::Handle<i::Object>(foreign_globals->get(j), isolate));
236       if (!name.is_null()) {
237         i::MaybeHandle<i::Object> val =
238             i::Object::GetProperty(foreign, name.ToHandleChecked());
239         if (!val.is_null()) {
240           foreign_args_array[j] = val.ToHandleChecked();
241           continue;
242         }
243       }
244     }
245     foreign_args_array[j] = undefined;
246   }
247   i::MaybeHandle<i::Object> retval = i::Execution::Call(
248       isolate, init, undefined, foreign_globals->length(), foreign_args_array);
249   delete[] foreign_args_array;
250   DCHECK(!retval.is_null());
251 
252   i::Handle<i::Name> single_function_name(
253       isolate->factory()->InternalizeUtf8String(
254           wasm::AsmWasmBuilder::single_function_name));
255   i::MaybeHandle<i::Object> single_function =
256       i::Object::GetProperty(module_object, single_function_name);
257   if (!single_function.is_null() &&
258       !single_function.ToHandleChecked()->IsUndefined(isolate)) {
259     return single_function;
260   }
261   return module_object;
262 }
263 
264 }  // namespace internal
265 }  // namespace v8
266