Bug 965880 - OdinMonkey: allow typed array constructors to be imported and used (r=bbouvier)
authorLuke Wagner <luke@mozilla.com>
Tue, 07 Oct 2014 14:07:49 -0500
changeset 232448 09bd9d93d3e2574abdc5db3f608737a309eb8d59
parent 232447 c5e310d17e58610b6f1b1b13779a98a1ccc1acb4
child 232449 f72b6d7ece75f3824d6322dc011a41ef5c049c2d
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs965880
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 965880 - OdinMonkey: allow typed array constructors to be imported and used (r=bbouvier)
js/src/asmjs/AsmJSLink.cpp
js/src/asmjs/AsmJSModule.h
js/src/asmjs/AsmJSValidate.cpp
js/src/jit-test/tests/asm.js/testResize.js
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -214,20 +214,22 @@ ValidateFFI(JSContext *cx, AsmJSModule::
     if (!v.isObject() || !v.toObject().is<JSFunction>())
         return LinkFail(cx, "FFI imports must be functions");
 
     (*ffis)[global.ffiIndex()].set(&v.toObject().as<JSFunction>());
     return true;
 }
 
 static bool
-ValidateArrayView(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal,
-                  HandleValue bufferVal)
+ValidateArrayView(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
 {
-    RootedPropertyName field(cx, global.viewName());
+    RootedPropertyName field(cx, global.maybeViewName());
+    if (!field)
+        return true;
+
     RootedValue v(cx);
     if (!GetDataProperty(cx, globalVal, field, &v))
         return false;
 
     if (!IsTypedArrayConstructor(v, global.viewType()) &&
         !IsSharedTypedArrayConstructor(v, global.viewType()))
     {
         return LinkFail(cx, "bad typed array constructor");
@@ -503,17 +505,18 @@ DynamicallyLinkModule(JSContext *cx, Cal
             if (!ValidateGlobalVariable(cx, module, global, importVal))
                 return false;
             break;
           case AsmJSModule::Global::FFI:
             if (!ValidateFFI(cx, global, importVal, &ffis))
                 return false;
             break;
           case AsmJSModule::Global::ArrayView:
-            if (!ValidateArrayView(cx, global, globalVal, bufferVal))
+          case AsmJSModule::Global::ArrayViewCtor:
+            if (!ValidateArrayView(cx, global, globalVal))
                 return false;
             break;
           case AsmJSModule::Global::MathBuiltinFunction:
             if (!ValidateMathBuiltinFunction(cx, global, globalVal))
                 return false;
             break;
           case AsmJSModule::Global::Constant:
             if (!ValidateConstant(cx, global, globalVal))
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -195,17 +195,17 @@ class AsmJSNumLit
 //
 // NB: this means that AsmJSModule must be GC-safe.
 class AsmJSModule
 {
   public:
     class Global
     {
       public:
-        enum Which { Variable, FFI, ArrayView, MathBuiltinFunction, Constant,
+        enum Which { Variable, FFI, ArrayView, ArrayViewCtor, MathBuiltinFunction, Constant,
                      SimdCtor, SimdOperation};
         enum VarInitKind { InitConstant, InitImport };
         enum ConstantKind { GlobalConstant, MathConstant };
 
       private:
         struct Pod {
             Which which_;
             union {
@@ -279,22 +279,26 @@ class AsmJSModule
         PropertyName *ffiField() const {
             MOZ_ASSERT(pod.which_ == FFI);
             return name_;
         }
         uint32_t ffiIndex() const {
             MOZ_ASSERT(pod.which_ == FFI);
             return pod.u.ffiIndex_;
         }
-        PropertyName *viewName() const {
-            MOZ_ASSERT(pod.which_ == ArrayView);
+        // When a view is created from an imported constructor:
+        //   var I32 = stdlib.Int32Array;
+        //   var i32 = new I32(buffer);
+        // the second import has nothing to validate and thus has a null field.
+        PropertyName *maybeViewName() const {
+            MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
             return name_;
         }
         Scalar::Type viewType() const {
-            MOZ_ASSERT(pod.which_ == ArrayView);
+            MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
             return pod.u.viewType_;
         }
         PropertyName *mathName() const {
             MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
             return name_;
         }
         AsmJSMathBuiltinFunction mathBuiltinFunction() const {
             MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
@@ -938,20 +942,27 @@ class AsmJSModule
     bool addFFI(PropertyName *field, uint32_t *ffiIndex) {
         MOZ_ASSERT(!isFinishedWithModulePrologue());
         if (pod.numFFIs_ == UINT32_MAX)
             return false;
         Global g(Global::FFI, field);
         g.pod.u.ffiIndex_ = *ffiIndex = pod.numFFIs_++;
         return globals_.append(g);
     }
-    bool addArrayView(Scalar::Type vt, PropertyName *field) {
+    bool addArrayView(Scalar::Type vt, PropertyName *maybeField) {
         MOZ_ASSERT(!isFinishedWithModulePrologue());
         pod.hasArrayView_ = true;
-        Global g(Global::ArrayView, field);
+        Global g(Global::ArrayView, maybeField);
+        g.pod.u.viewType_ = vt;
+        return globals_.append(g);
+    }
+    bool addArrayViewCtor(Scalar::Type vt, PropertyName *field) {
+        MOZ_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(field);
+        Global g(Global::ArrayViewCtor, field);
         g.pod.u.viewType_ = vt;
         return globals_.append(g);
     }
     bool addMathBuiltinFunction(AsmJSMathBuiltinFunction func, PropertyName *field) {
         MOZ_ASSERT(!isFinishedWithModulePrologue());
         Global g(Global::MathBuiltinFunction, field);
         g.pod.u.mathBuiltinFunc_ = func;
         return globals_.append(g);
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -1081,16 +1081,17 @@ class MOZ_STACK_CLASS ModuleCompiler
         enum Which {
             Variable,
             ConstantLiteral,
             ConstantImport,
             Function,
             FuncPtrTable,
             FFI,
             ArrayView,
+            ArrayViewCtor,
             MathBuiltinFunction,
             SimdCtor,
             SimdOperation
         };
 
       private:
         Which which_;
         union {
@@ -1143,17 +1144,17 @@ class MOZ_STACK_CLASS ModuleCompiler
             MOZ_ASSERT(which_ == FuncPtrTable);
             return u.funcPtrTableIndex_;
         }
         unsigned ffiIndex() const {
             MOZ_ASSERT(which_ == FFI);
             return u.ffiIndex_;
         }
         Scalar::Type viewType() const {
-            MOZ_ASSERT(which_ == ArrayView);
+            MOZ_ASSERT(which_ == ArrayView || which_ == ArrayViewCtor);
             return u.viewType_;
         }
         bool isMathFunction() const {
             return which_ == MathBuiltinFunction;
         }
         AsmJSMathBuiltinFunction mathBuiltinFunction() const {
             MOZ_ASSERT(which_ == MathBuiltinFunction);
             return u.mathBuiltinFunc_;
@@ -1618,21 +1619,30 @@ class MOZ_STACK_CLASS ModuleCompiler
         if (!global)
             return false;
         uint32_t index;
         if (!module_->addFFI(field, &index))
             return false;
         global->u.ffiIndex_ = index;
         return globals_.putNew(varName, global);
     }
-    bool addArrayView(PropertyName *varName, Scalar::Type vt, PropertyName *fieldName) {
+    bool addArrayView(PropertyName *varName, Scalar::Type vt, PropertyName *maybeField) {
         Global *global = moduleLifo_.new_<Global>(Global::ArrayView);
         if (!global)
             return false;
-        if (!module_->addArrayView(vt, fieldName))
+        if (!module_->addArrayView(vt, maybeField))
+            return false;
+        global->u.viewType_ = vt;
+        return globals_.putNew(varName, global);
+    }
+    bool addArrayViewCtor(PropertyName *varName, Scalar::Type vt, PropertyName *fieldName) {
+        Global *global = moduleLifo_.new_<Global>(Global::ArrayViewCtor);
+        if (!global)
+            return false;
+        if (!module_->addArrayViewCtor(vt, fieldName))
             return false;
         global->u.viewType_ = vt;
         return globals_.putNew(varName, global);
     }
     bool addMathBuiltinFunction(PropertyName *varName, AsmJSMathBuiltinFunction func, PropertyName *fieldName) {
         if (!module_->addMathBuiltinFunction(func, fieldName))
             return false;
         Global *global = moduleLifo_.new_<Global>(Global::MathBuiltinFunction);
@@ -3495,62 +3505,87 @@ CheckGlobalVariableInitImport(ModuleComp
     AsmJSCoercion coercion;
     ParseNode *coercedExpr;
     if (!CheckTypeAnnotation(m, initNode, &coercion, &coercedExpr))
         return false;
     return CheckGlobalVariableImportExpr(m, varName, coercion, coercedExpr, isConst);
 }
 
 static bool
+IsArrayViewCtorName(ModuleCompiler &m, PropertyName *name, Scalar::Type *type)
+{
+    JSAtomState &names = m.cx()->names();
+    if (name == names.Int8Array || name == names.SharedInt8Array)
+        *type = Scalar::Int8;
+    else if (name == names.Uint8Array || name == names.SharedUint8Array)
+        *type = Scalar::Uint8;
+    else if (name == names.Int16Array || name == names.SharedInt16Array)
+        *type = Scalar::Int16;
+    else if (name == names.Uint16Array || name == names.SharedUint16Array)
+        *type = Scalar::Uint16;
+    else if (name == names.Int32Array || name == names.SharedInt32Array)
+        *type = Scalar::Int32;
+    else if (name == names.Uint32Array || name == names.SharedUint32Array)
+        *type = Scalar::Uint32;
+    else if (name == names.Float32Array || name == names.SharedFloat32Array)
+        *type = Scalar::Float32;
+    else if (name == names.Float64Array || name == names.SharedFloat64Array)
+        *type = Scalar::Float64;
+    else
+        return false;
+    return true;
+}
+
+static bool
 CheckNewArrayView(ModuleCompiler &m, PropertyName *varName, ParseNode *newExpr)
 {
-    ParseNode *ctorExpr = ListHead(newExpr);
-    if (!ctorExpr->isKind(PNK_DOT))
-        return m.fail(ctorExpr, "only valid 'new' import is 'new global.*Array(buf)'");
-
-    ParseNode *base = DotBase(ctorExpr);
-    PropertyName *field = DotMember(ctorExpr);
-
     PropertyName *globalName = m.module().globalArgumentName();
     if (!globalName)
-        return m.fail(base, "cannot create array view without an asm.js global parameter");
-    if (!IsUseOfName(base, globalName))
-        return m.failName(base, "expecting '%s.*Array", globalName);
+        return m.fail(newExpr, "cannot create array view without an asm.js global parameter");
+
+    PropertyName *bufferName = m.module().bufferArgumentName();
+    if (!bufferName)
+        return m.fail(newExpr, "cannot create array view without an asm.js heap parameter");
+
+    ParseNode *ctorExpr = ListHead(newExpr);
+
+    PropertyName *field;
+    Scalar::Type type;
+    if (ctorExpr->isKind(PNK_DOT)) {
+        ParseNode *base = DotBase(ctorExpr);
+
+        if (!IsUseOfName(base, globalName))
+            return m.failName(base, "expecting '%s.*Array", globalName);
+
+        field = DotMember(ctorExpr);
+        if (!IsArrayViewCtorName(m, field, &type))
+            return m.fail(ctorExpr, "could not match typed array name");
+    } else {
+        if (!ctorExpr->isKind(PNK_NAME))
+            return m.fail(ctorExpr, "expecting name of imported array view constructor");
+
+        PropertyName *globalName = ctorExpr->name();
+        const ModuleCompiler::Global *global = m.lookupGlobal(globalName);
+        if (!global)
+            return m.failName(ctorExpr, "%s not found in module global scope", globalName);
+
+        if (global->which() != ModuleCompiler::Global::ArrayViewCtor)
+            return m.failName(ctorExpr, "%s must be an imported array view constructor", globalName);
+
+        field = nullptr;
+        type = global->viewType();
+    }
 
     ParseNode *bufArg = NextNode(ctorExpr);
     if (!bufArg || NextNode(bufArg) != nullptr)
         return m.fail(ctorExpr, "array view constructor takes exactly one argument");
 
-    PropertyName *bufferName = m.module().bufferArgumentName();
-    if (!bufferName)
-        return m.fail(bufArg, "cannot create array view without an asm.js heap parameter");
     if (!IsUseOfName(bufArg, bufferName))
         return m.failName(bufArg, "argument to array view constructor must be '%s'", bufferName);
 
-    JSAtomState &names = m.cx()->names();
-    Scalar::Type type;
-    if (field == names.Int8Array || field == names.SharedInt8Array)
-        type = Scalar::Int8;
-    else if (field == names.Uint8Array || field == names.SharedUint8Array)
-        type = Scalar::Uint8;
-    else if (field == names.Int16Array || field == names.SharedInt16Array)
-        type = Scalar::Int16;
-    else if (field == names.Uint16Array || field == names.SharedUint16Array)
-        type = Scalar::Uint16;
-    else if (field == names.Int32Array || field == names.SharedInt32Array)
-        type = Scalar::Int32;
-    else if (field == names.Uint32Array || field == names.SharedUint32Array)
-        type = Scalar::Uint32;
-    else if (field == names.Float32Array || field == names.SharedFloat32Array)
-        type = Scalar::Float32;
-    else if (field == names.Float64Array || field == names.SharedFloat64Array)
-        type = Scalar::Float64;
-    else
-        return m.fail(ctorExpr, "could not match typed array name");
-
     return m.addArrayView(varName, type, field);
 }
 
 static bool
 IsSimdTypeName(ModuleCompiler &m, PropertyName *name, AsmJSSimdType *type)
 {
     if (name == m.cx()->names().int32x4) {
         *type = AsmJSSimdType_int32x4;
@@ -3659,17 +3694,22 @@ CheckGlobalDotImport(ModuleCompiler &m, 
     if (!base->isKind(PNK_NAME))
         return m.fail(base, "expected name of variable or parameter");
 
     if (base->name() == m.module().globalArgumentName()) {
         if (field == m.cx()->names().NaN)
             return m.addGlobalConstant(varName, GenericNaN(), field);
         if (field == m.cx()->names().Infinity)
             return m.addGlobalConstant(varName, PositiveInfinity<double>(), field);
-        return m.failName(initNode, "'%s' is not a standard global constant", field);
+
+        Scalar::Type type;
+        if (IsArrayViewCtorName(m, field, &type))
+            return m.addArrayViewCtor(varName, type, field);
+
+        return m.failName(initNode, "'%s' is not a standard constant or typed array name", field);
     }
 
     if (base->name() == m.module().importArgumentName())
         return m.addFFI(varName, field);
 
     const ModuleCompiler::Global *global = m.lookupGlobal(base->name());
     if (!global)
         return m.failName(initNode, "%s not found in module global scope", base->name());
@@ -3925,16 +3965,17 @@ CheckVarRef(FunctionCompiler &f, ParseNo
             *def = f.loadGlobalVar(*global);
             *type = global->varOrConstType().toType();
             break;
           case ModuleCompiler::Global::Function:
           case ModuleCompiler::Global::FFI:
           case ModuleCompiler::Global::MathBuiltinFunction:
           case ModuleCompiler::Global::FuncPtrTable:
           case ModuleCompiler::Global::ArrayView:
+          case ModuleCompiler::Global::ArrayViewCtor:
           case ModuleCompiler::Global::SimdCtor:
           case ModuleCompiler::Global::SimdOperation:
             return f.failName(varRef, "'%s' may not be accessed by ordinary expressions", name);
         }
         return true;
     }
 
     return f.failName(varRef, "'%s' not found in local or asm.js module scope", name);
@@ -5181,16 +5222,17 @@ CheckCoercedCall(FunctionCompiler &f, Pa
             return CheckFFICall(f, call, global->ffiIndex(), retType, def, type);
           case ModuleCompiler::Global::MathBuiltinFunction:
             return CheckCoercedMathBuiltinCall(f, call, global->mathBuiltinFunction(), retType, def, type);
           case ModuleCompiler::Global::ConstantLiteral:
           case ModuleCompiler::Global::ConstantImport:
           case ModuleCompiler::Global::Variable:
           case ModuleCompiler::Global::FuncPtrTable:
           case ModuleCompiler::Global::ArrayView:
+          case ModuleCompiler::Global::ArrayViewCtor:
             return f.failName(callee, "'%s' is not callable function", callee->name());
           case ModuleCompiler::Global::SimdCtor:
           case ModuleCompiler::Global::SimdOperation:
             return CheckCoercedSimdCall(f, call, global, retType, def, type);
           case ModuleCompiler::Global::Function:
             break;
         }
     }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/testResize.js
@@ -0,0 +1,32 @@
+load(libdir + "asm.js");
+
+assertAsmTypeFail('glob', USE_ASM + "var I32=glob.Int32Arra; function f() {} return f");
+var m = asmCompile('glob', USE_ASM + "var I32=glob.Int32Array; function f() {} return f");
+assertAsmLinkFail(m, {});
+assertAsmLinkFail(m, {Int32Array:null});
+assertAsmLinkFail(m, {Int32Array:{}});
+assertAsmLinkFail(m, {Int32Array:Uint32Array});
+assertEq(asmLink(m, {Int32Array:Int32Array})(), undefined);
+var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + "var I32=glob.Int32Array; function f() {} return f");
+assertEq(asmLink(m, this)(), undefined);
+assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);
+
+assertAsmTypeFail('glob', 'ffis', 'buf', USE_ASM + 'var I32=glob.Int32Array; var i32=new I3(buf); function f() {} return f');
+assertAsmTypeFail('glob', 'ffis', 'buf', USE_ASM + 'var I32=0; var i32=new I32(buf); function f() {} return f');
+var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var I32=glob.Int32Array; var i32=new I32(buf); function f() {} return f');
+assertAsmLinkFail(m, this, null, {});
+assertAsmLinkAlwaysFail(m, this, null, null);
+assertAsmLinkFail(m, this, null, new ArrayBuffer(100));
+assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);
+
+var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var I32=glob.Int32Array; var i32=new glob.Int32Array(buf); function f() {} return f');
+assertAsmLinkFail(m, this, null, {});
+assertAsmLinkAlwaysFail(m, this, null, null);
+assertAsmLinkFail(m, this, null, new ArrayBuffer(100));
+assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);
+
+var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var F32=glob.Float32Array; var i32=new glob.Int32Array(buf); function f() {} return f');
+assertAsmLinkFail(m, this, null, {});
+assertAsmLinkAlwaysFail(m, this, null, null);
+assertAsmLinkFail(m, this, null, new ArrayBuffer(100));
+assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);