From 60c7b3b0ca85570a94b2f155d9991a17e80ede26 Mon Sep 17 00:00:00 2001 From: dcode Date: Fri, 27 Mar 2020 09:09:11 +0100 Subject: [PATCH] Always register non-MVP types and check on actual use --- src/compiler.ts | 96 ++++++++++++++++++++-- src/program.ts | 8 +- tests/compiler/features/not-supported.json | 32 ++++++++ tests/compiler/features/not-supported.ts | 35 ++++++++ 4 files changed, 164 insertions(+), 7 deletions(-) create mode 100644 tests/compiler/features/not-supported.json create mode 100644 tests/compiler/features/not-supported.ts diff --git a/src/compiler.ts b/src/compiler.ts index 495f670822..0f0d528ebb 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -60,7 +60,8 @@ import { CommonNames, INDEX_SUFFIX, Feature, - Target + Target, + featureToString } from "./common"; import { @@ -113,6 +114,7 @@ import { DecoratorKind, AssertionKind, SourceKind, + FunctionTypeNode, Statement, BlockStatement, @@ -961,6 +963,7 @@ export class Compiler extends DiagnosticEmitter { return false; } global.setType(resolvedType); + this.checkTypeSupported(global.type, typeNode); // Otherwise infer type from initializer } else if (initializerNode) { @@ -1268,6 +1271,9 @@ export class Compiler extends DiagnosticEmitter { var module = this.module; var signature = instance.signature; var bodyNode = instance.prototype.bodyNode; + var declarationNode = instance.declaration; + assert(declarationNode.kind == NodeKind.FUNCTIONDECLARATION || declarationNode.kind == NodeKind.METHODDECLARATION); + this.checkSignatureSupported(instance.signature, (declarationNode).signature); var funcRef: FunctionRef; @@ -1343,7 +1349,7 @@ export class Compiler extends DiagnosticEmitter { // imported function } else if (instance.is(CommonFlags.AMBIENT)) { instance.set(CommonFlags.MODULE_IMPORT); - mangleImportName(instance, instance.declaration); // TODO: check for duplicates + mangleImportName(instance, declarationNode); // TODO: check for duplicates module.addFunctionImport( instance.internalName, mangleImportName_moduleName, @@ -1576,7 +1582,12 @@ export class Compiler extends DiagnosticEmitter { ); if (type.isManaged) valueExpr = this.makeRetain(valueExpr); instance.getterRef = module.addFunction(instance.internalGetterName, nativeThisType, nativeValueType, null, valueExpr); - if (instance.setterRef) instance.set(CommonFlags.COMPILED); + if (instance.setterRef) { + instance.set(CommonFlags.COMPILED); + } else { + let typeNode = instance.typeNode; + if (typeNode) this.checkTypeSupported(instance.type, typeNode); + } return true; } @@ -1624,7 +1635,12 @@ export class Compiler extends DiagnosticEmitter { nativeValueType, instance.memoryOffset ) ); - if (instance.getterRef) instance.set(CommonFlags.COMPILED); + if (instance.getterRef) { + instance.set(CommonFlags.COMPILED); + } else { + let typeNode = instance.typeNode; + if (typeNode) this.checkTypeSupported(instance.type, typeNode); + } return true; } @@ -2856,6 +2872,8 @@ export class Compiler extends DiagnosticEmitter { makeMap(flow.contextualTypeArguments) ); if (!type) continue; + this.checkTypeSupported(type, typeNode); + if (initializerNode) { initExpr = this.compileExpression(initializerNode, type, // reports Constraints.CONV_IMPLICIT | Constraints.WILL_RETAIN @@ -6228,6 +6246,12 @@ export class Compiler extends DiagnosticEmitter { var thisType = (field.parent).type; var nativeThisType = thisType.toNativeType(); + if (!field.is(CommonFlags.COMPILED)) { + field.set(CommonFlags.COMPILED); + let typeNode = field.typeNode; + if (typeNode) this.checkTypeSupported(field.type, typeNode); + } + if (fieldType.isManaged && thisType.isManaged) { let tempThis = flow.getTempLocal(thisType, findUsedLocals(valueExpr)); // set before and read after valueExpr executes below ^ @@ -8818,7 +8842,6 @@ export class Compiler extends DiagnosticEmitter { for (let i = 0; i < numParameters; ++i) { operands[i + 1] = module.local_get(i + 1, parameterTypes[i].toNativeType()); } - // TODO: base constructor might be inlined, but makeCallDirect can't do this stmts.push( module.local_set(0, this.makeCallDirect(assert(baseClass.constructorInstance), operands, reportNode, false, true) @@ -8927,6 +8950,11 @@ export class Compiler extends DiagnosticEmitter { ); } } + if (!fieldInstance.is(CommonFlags.COMPILED)) { + fieldInstance.set(CommonFlags.COMPILED); + let typeNode = fieldInstance.typeNode; + if (typeNode) this.checkTypeSupported(fieldInstance.type, typeNode); + } this.currentType = fieldType; return module.load( fieldType.byteSize, @@ -9849,6 +9877,62 @@ export class Compiler extends DiagnosticEmitter { parentFunction.debugLocations.push(range); } + /** Checks whether a particular feature is enabled. */ + checkFeatureEnabled(feature: Feature, reportNode: Node): bool { + if (!this.options.hasFeature(feature)) { + this.error( + DiagnosticCode.Feature_0_is_not_enabled, + reportNode.range, featureToString(feature) + ); + return false; + } + return true; + } + + /** Checks whether a particular type is supported. */ + checkTypeSupported(type: Type, reportNode: Node): bool { + switch (type.kind) { + case TypeKind.V128: return this.checkFeatureEnabled(Feature.SIMD, reportNode); + case TypeKind.ANYREF: return this.checkFeatureEnabled(Feature.REFERENCE_TYPES, reportNode); + } + if (type.is(TypeFlags.REFERENCE)) { + let classReference = type.classReference; + while (classReference) { + let typeArguments = classReference.typeArguments; + if (typeArguments) { + for (let i = 0, k = typeArguments.length; i < k; ++i) { + if (!this.checkTypeSupported(typeArguments[i], reportNode)) { + return false; + } + } + } + classReference = classReference.base; + } + } + return true; + } + + /** Checks whether a particular function signature is supported. */ + checkSignatureSupported(signature: Signature, reportNode: FunctionTypeNode): bool { + var supported = true; + var explicitThisType = reportNode.explicitThisType; + if (explicitThisType) { + if (!this.checkTypeSupported(assert(signature.thisType), explicitThisType)) { + supported = false; + } + } + var parameterTypes = signature.parameterTypes; + for (let i = 0, k = parameterTypes.length; i < k; ++i) { + if (!this.checkTypeSupported(parameterTypes[i], reportNode.parameters[i])) { + supported = false; + } + } + if (!this.checkTypeSupported(signature.returnType, reportNode.returnType)) { + supported = false; + } + return supported; + } + // === Specialized code generation ============================================================== /** Makes a constant zero of the specified type. */ @@ -10039,6 +10123,8 @@ export class Compiler extends DiagnosticEmitter { let initializerNode = fieldPrototype.initializerNode; let parameterIndex = fieldPrototype.parameterIndex; let initExpr: ExpressionRef; + let typeNode = field.typeNode; + if (typeNode) this.checkTypeSupported(fieldType, typeNode); // if declared as a constructor parameter, use its value if (parameterIndex >= 0) { diff --git a/src/program.ts b/src/program.ts index 6612ae778c..cc6b9a00f7 100644 --- a/src/program.ts +++ b/src/program.ts @@ -725,8 +725,12 @@ export class Program extends DiagnosticEmitter { this.makeNativeTypeDeclaration(CommonNames.returnof, CommonFlags.EXPORT | CommonFlags.GENERIC), DecoratorFlags.BUILTIN )); - if (options.hasFeature(Feature.SIMD)) this.registerNativeType(CommonNames.v128, Type.v128); - if (options.hasFeature(Feature.REFERENCE_TYPES)) this.registerNativeType(CommonNames.anyref, Type.anyref); + + // The following types might not be enabled by compiler options, so the + // compiler needs to check this condition whenever such a value is created + // respectively stored or loaded. + this.registerNativeType(CommonNames.v128, Type.v128); + this.registerNativeType(CommonNames.anyref, Type.anyref); // register compiler hints this.registerConstantInteger(CommonNames.ASC_TARGET, Type.i32, diff --git a/tests/compiler/features/not-supported.json b/tests/compiler/features/not-supported.json new file mode 100644 index 0000000000..99f1dac543 --- /dev/null +++ b/tests/compiler/features/not-supported.json @@ -0,0 +1,32 @@ +{ + "asc_flags": [ + "--runtime none" + ], + "stderr": [ + "AS103: Feature 'simd' is not enabled.", + "var a: v128;", + "AS103: Feature 'simd' is not enabled.", + "var b = v128.", + "AS103: Feature 'simd' is not enabled.", + "var c: Array;", + "AS103: Feature 'simd' is not enabled.", + "var a: v128;", + "AS103: Feature 'simd' is not enabled.", + "var b = v128.", + "AS103: Feature 'simd' is not enabled.", + "let a: v128;", + "AS103: Feature 'simd' is not enabled.", + "let b = v128.", + "AS103: Feature 'simd' is not enabled.", + "a: v128", + "AS103: Feature 'simd' is not enabled.", + "): v128", + "AS103: Feature 'simd' is not enabled.", + "a: v128;", + "AS103: Feature 'simd' is not enabled.", + "b: Array;", + "AS103: Feature 'simd' is not enabled.", + "get c(): v128", + "EOF" + ] +} diff --git a/tests/compiler/features/not-supported.ts b/tests/compiler/features/not-supported.ts new file mode 100644 index 0000000000..81fe2a2eb4 --- /dev/null +++ b/tests/compiler/features/not-supported.ts @@ -0,0 +1,35 @@ +var a: v128; // type not enabled +var b = v128.splat(1); // instruction not enabled +var c: Array; // type not enabled + +function test1(): void { + var a: v128; // type not enabled + var b = v128.splat(1); // instruction not enabled +} +test1(); + +function test2(): void { + { + let a: v128; // type not enabled + let b = v128.splat(1); // instruction not enabled + } +} +test2(); + +function test3( + a: v128 // type not enabled +): v128 { // type not enabled + return unreachable(); +} +test3(v128.splat(1)); // instruction not enabled + +class Foo { + a: v128; // type not enabled + b: Array; // type not enabled + get c(): v128 { // type not enabled + return this.a; + } +} +(new Foo()).c; + +ERROR("EOF");